﻿using System.ComponentModel;
using System.Composition;
using System.Events;
using System.Reflection;
using System.Services;
using System.Threading.Tasks;

namespace System.Sensors
{
    /// <summary>
    /// Base class for all Sensor services
    /// </summary>
    /// <typeparam name="TSensor">Type of the Sensor <seealso cref="Windows.Devices.Sensors"/> namespace</typeparam>
    /// <typeparam name="TReadingData"></typeparam>
    public abstract class SensorServiceBase<TSensor, TReadingData> : NotificationObject, ISensorService<TSensor, TReadingData>
    {
        private TSensor m_Sensor;

        /// <summary>
        /// Sensor api
        /// </summary>
        public TSensor Sensor
        {
            get
            {
                return m_Sensor;
            }
            protected set
            {
                SetProperty<TSensor>(ref m_Sensor, value);
                NotifyPropertyChanged("IsSensorAvailable");
            }
        }

        /// <summary>
        /// Returns true if sensor is available
        /// </summary>
        public bool IsSensorAvailable
        {
            get { return Sensor != null; }
        }

        /// <summary>
        /// Minimal report interval sensor supported
        /// </summary>
        public uint MinimumReportInterval
        {
            get
            {
                return GetMinimumReportInterval();
            }
        }

        /// <summary>
        /// Current sensor's report interval
        /// </summary>
        public uint ReportInterval
        {
            get
            {
                return GetReportInterval();
            }
            set
            {
                SetReportIntervalInternal(value);
            }
        }

        /// <summary>
        /// Composed Messenger Service
        /// </summary>
        [Import]
        public IEventPublisher EventAggregator { get; set; }

        /// <summary>
        /// Composed Logger service
        /// </summary>
        [Import]
        public ILoggerService Logger { get; set; }

        bool m_isSubscribed = false;
        
        /// <summary>
        /// Returns true if we are currently subscribed for sensor data changes messages
        /// </summary>
        public bool IsSubscribed
        {
            get
            {
                return m_isSubscribed;
            }
            private set
            {
                SetProperty<bool>(ref m_isSubscribed, value);
                if (EventAggregator != null)
                    EventAggregator.Publish<SensorSubscriptionChangedBase>(new SensorSubscriptionChangedBase(value));
            }
        }

        /// <summary>
        /// Poll sensor's reading data
        /// </summary>
        /// <returns></returns>
        public async Task<TReadingData> PollData()
        {
            if (!IsSensorAvailable)
                return default(TReadingData);
            return await GetSensorData();
        }

        /// <summary>
        /// Sensor reading data polling implementation
        /// </summary>
        /// <returns></returns>
        protected abstract Task<TReadingData> GetSensorData();

        /// <summary>
        /// Subscribes for sensor data changes messages
        /// </summary>
        public virtual void Subscribe()
        {
            if (IsSensorAvailable)
            {
                Logger.Log(LogSeverity.Information, this.GetType().Name, "");
                IsSubscribed = true;
            }
        }

        /// <summary>
        /// Unsubscribes for sensor data changes messages
        /// </summary>
        public virtual void Unsubscribe()
        {
            if (IsSensorAvailable)
                IsSubscribed = false;
        }

        public virtual void StartService()
        {
            var attr = this.GetType().GetTypeInfo().GetCustomAttribute<AutoMessagePublishingAttribute>(true);
            if (attr != null)
            {
                Subscribe();
            }
        }

        public virtual void StopService()
        {
        }

        public virtual void Dispose()
        {
            if (IsSubscribed)
                Unsubscribe();
        }

        public abstract uint GetMinimumReportInterval();
        public abstract uint GetReportInterval();
        public abstract void SetReportInterval(uint value);

        private void SetReportIntervalInternal(uint value)
        {
            if (ReportInterval < MinimumReportInterval)
                value = MinimumReportInterval;

            if (ReportInterval == value)
                return;

            SetReportInterval(value);
            NotifyPropertyChanged("ReportInterval");
        }
    }
}
